package com.matrix.gui;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.view.View;

/**
 * MapDrawableView
 * Creates and draws the heat map to a canvas
 * @author McGarvie
 *
 */
public class MapDrawableView extends View{
	private Context mContext;
	/**
	 * Creates the gradient of witch the heat map will use
	 */
	private Paint[] gradient;
	//width is the width in number of pixels across the screen
	/**
	 * the width in pixels across the screen
	 */
	private int width;
	/**
	 * height in pixels
	 */
	private int height;
	//the matrix passed through to this view
	/**
	 * the matrix to create a heatmap for
	 */
	private com.matrix.math.Matrix matrix;
	/**
	 * the colors for each matrix
	 */
	private int[][] dataColorIndices;
	 
	/**
	 * Constructs and sets up the heat map
	 * @param context
	 * @param m
	 * @param w
	 * @param h
	 */
	public MapDrawableView(Context context, com.matrix.math.Matrix m, int w, int h) 
	{
		super(context);
		mContext = context;
		matrix = m;
		width = w-40;
		height = h-40;
		//sets the levels 
		gradient = this.createGradient();
		this.setBackgroundColor(Color.WHITE);
		setLevels();
	}
	
	/**
	 * draws the map to the canvas
	 */
	protected void onDraw(Canvas canvas)
	{
		//uses the width of the screen to adjust the size of each element of the heat map
		float multw = (float) (width/(matrix.numCols()));
		float multh =(float) (width/(matrix.numRows()));
		for(int i=0; i<matrix.numRows(); i++)
		{
			for(int j=0; j<matrix.numCols(); j++)
			{
				try {
					canvas.drawRect(18+j*multw, 18+i*multh, 18+multw+j*multw, 18+multh+i*multh, gradient[dataColorIndices[i][j]]);
				} catch (Exception e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
		invalidate();
	}
	/**
	 * Sets the color for each element of the matrix
	 * uses algorithm for setting the color for the elements of the array from 
	 * http://www.mbeckler.org/heatMap/
	 */
	private void setLevels()
	{
		double max;
		try {
			max = matrix.getValueAt(0, 0);
			double min = matrix.getValueAt(0, 0);
			for(int i=0; i<matrix.numRows(); i++)
			{
				for(int j=0; j<matrix.numCols(); j++)
				{
					if(max<matrix.getValueAt(i, j))
					{
						max = matrix.getValueAt(i, j);
					}
					if(min>matrix.getValueAt(i, j))
					{
						min = matrix.getValueAt(i, j);
					}
				}
			}
			double diff = (max - min);
			dataColorIndices = new int[matrix.numRows()][matrix.numCols()];    

	        //assign a Color to each data point
	        for (int x = 0; x < matrix.numRows(); x++)
	        {
	            for (int y = 0; y < matrix.numCols(); y++)
	            {
	                double norm = 1-((matrix.getValueAt(x, y) - min) / diff); // 0 < norm < 1
	                int colorIndex = (int) Math.floor(norm * (gradient.length - 1));
	                dataColorIndices[x][y] = colorIndex;
	            }
	        }
		}catch(Exception e)
		{
			
		}
			
	}
	/**
	 * creates the gradient between 5 different colors
	 * algorithm gotten from http://www.mbeckler.org/heatMap/
	 * and changed to work with within android and not to be just between two colors
	 * @return
	 */
	public Paint[] createGradient()
    {
		//gets gradient between the first set and adds them to the gradient array
        int r1 = 237;
        int g1 = 28;
        int b1 = 36;
        int a1 = 255;

        int r2 = 255;
        int g2 = 127;
        int b2 = 39;
        int a2 = 255;

        int newR = 0;
        int newG = 0;
        int newB = 0;
        int newA = 0;
        int numSteps = 10;
        Paint[] gradient = new Paint[numSteps*4];
        double iNorm;
        for (int i = 0; i <numSteps; i++)
        {
            iNorm = i / (double)numSteps; //a normalized [0:1] variable
            newR = (int) (r1 + iNorm * (r2 - r1));
            newG = (int) (g1 + iNorm * (g2 - g1));
            newB = (int) (b1 + iNorm * (b2 - b1));
            newA = (int) (a1 + iNorm * (a2 - a1));
            gradient[i] = new Paint();
            gradient[i].setARGB(newA, newR, newG, newB);
        }
        //gets gradient between the low one of the last one and another one
        r1 = 255;
        g1 = 127;
        b1 = 39;
        a1 = 255;

        r2 = 255;
        g2 = 242;
        b2 = 0;
        a2 = 255;

        newR = 0;
        newG = 0;
        newB = 0;
        newA = 0;
        int j=0;
        for (int i = numSteps-1; i <numSteps*2; i++)
        {
            iNorm = j / (double)numSteps; //a normalized [0:1] variable
            newR = (int) (r1 + iNorm * (r2 - r1));
            newG = (int) (g1 + iNorm * (g2 - g1));
            newB = (int) (b1 + iNorm * (b2 - b1));
            newA = (int) (a1 + iNorm * (a2 - a1));
            gradient[i] = new Paint();
            gradient[i].setARGB(newA, newR, newG, newB);
            j++;
        }
        
        r1 = 255;
        g1 = 242;
        b1 = 0;
        a1 = 255;

        r2 = 181;
        g2 = 230;
        b2 = 29;
        a2 = 255;

        newR = 0;
        newG = 0;
        newB = 0;
        newA = 0;
        j=0;
        for (int i = numSteps*2-1; i <numSteps*3; i++)
        {
            iNorm = j / (double)numSteps; //a normalized [0:1] variable
            newR = (int) (r1 + iNorm * (r2 - r1));
            newG = (int) (g1 + iNorm * (g2 - g1));
            newB = (int) (b1 + iNorm * (b2 - b1));
            newA = (int) (a1 + iNorm * (a2 - a1));
            gradient[i] = new Paint();
            gradient[i].setARGB(newA, newR, newG, newB);
            j++;
        }
        
        r1 = 181;
        g1 = 230;
        b1 = 29;
        a1 = 255;

        r2 = 153;
        g2 = 217;
        b2 = 234;
        a2 = 255;

        newR = 0;
        newG = 0;
        newB = 0;
        newA = 0;
        j=0;
        for (int i = numSteps*3-1; i <numSteps*4; i++)
        {
            iNorm = j / (double)numSteps; //a normalized [0:1] variable
            newR = (int) (r1 + iNorm * (r2 - r1));
            newG = (int) (g1 + iNorm * (g2 - g1));
            newB = (int) (b1 + iNorm * (b2 - b1));
            newA = (int) (a1 + iNorm * (a2 - a1));
            gradient[i] = new Paint();
            gradient[i].setARGB(newA, newR, newG, newB);
            j++;
        }
        
        

        return gradient;
    }
	
}

